﻿//========================================================= 
/**@file JniInvoker.h
 * @brief Java Jni辅助对象 
 * 
 * @date 2015-07-09   15:03:17
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_JNIINVOKER_H_
#define _LIBZHOUYB_JNIINVOKER_H_
//--------------------------------------------------------- 
#include <jni.h>
#include <vector>

#include "../../include/Base.h"
//--------------------------------------------------------- 
namespace zhou_yb {
namespace extension {
namespace ability {
//--------------------------------------------------------- 
/// Java Env生成器(C线程调用Java接口)
class JniEnvInvoker
{
protected:
    shared_obj<JavaVM*> _jVM;
    shared_obj<JNIEnv*> _env;

    void _init()
    {
        _jVM.obj() = NULL;
        _env.obj() = NULL;
    }
public:
    JniEnvInvoker(JNIEnv* env = NULL)
    {
        _init();
        Create(env);
    }
    virtual ~JniEnvInvoker()
    {
        Dispose();
    }

    bool Create(JavaVM* jVM)
    {
        if(jVM == NULL)
            return false;

        #ifdef _WIN32
        void* pEnv = NULL;
        #else
        JNIEnv* pEnv = NULL;
        #endif

        if(jVM->AttachCurrentThread(&pEnv, NULL) != JNI_OK)
            return false;

        Dispose();

        _jVM.obj() = jVM;
        _env.obj() = reinterpret_cast<JNIEnv*>(pEnv);
        return true;
    }
    bool Create(JNIEnv* env)
    {
        if(env == NULL)
            return false;
        JavaVM* jVM = NULL;
        env->GetJavaVM(&jVM);
        return Create(jVM);
    }
    inline bool IsValid() const
    {
        return _env.obj() != NULL && _jVM.obj() != NULL;
    }
    void Dispose()
    {
        // 只有自身拥有对象 
        if(IsValid() && _jVM.ref_count() < 2)
        {
            _jVM.obj()->DetachCurrentThread();
        }
        _jVM.reset() = NULL;
        _env.reset() = NULL;
    }

    inline operator JNIEnv* () { return _env.obj(); }
    inline operator const JNIEnv* () const { return _env.obj(); }

    inline JNIEnv& operator* () { return *(_env.obj()); }
    inline JNIEnv* operator->() { return &(operator *()); }
};
//--------------------------------------------------------- 
/// Java数据格式转换器 
class JniConverter
{
protected:
    shared_obj<JNIEnv*> _env;
public:
    JniConverter(JNIEnv* env) { _env.obj() = env; }

    size_t get_jbyteArray(jbyteArray _jbyteArray, size_t len, ByteBuilder& _cbyteArray)
    {
        size_t lastLen = _cbyteArray.GetLength();
        _cbyteArray.Append(static_cast<byte>(0x00), len);

        jbyte* jArray = reinterpret_cast<jbyte*>(const_cast<byte*>(_cbyteArray.GetBuffer(lastLen)));
        _env.obj()->GetByteArrayRegion(_jbyteArray, 0, len, jArray);
        return len;
    }
    inline size_t get_jbyteArray(jbyteArray _jbyteArray, ByteBuilder& _cbyteArray)
    {
        jsize len = _env.obj()->GetArrayLength(_jbyteArray);
        return get_jbyteArray(_jbyteArray, static_cast<size_t>(len), _cbyteArray);
    }
    size_t set_jbyteArray(const ByteArray& _cbyteArray, jbyteArray _jbyteArray)
    {
        jbyte* cArray = reinterpret_cast<jbyte*>(const_cast<byte*>(_cbyteArray.GetBuffer()));
        jsize clen = static_cast<jsize>(_cbyteArray.GetLength());
        jsize jlen = _env.obj()->GetArrayLength(_jbyteArray);
        clen = _min(clen, jlen);
        _env.obj()->SetByteArrayRegion(_jbyteArray, 0, clen, cArray);

        return clen;
    }
    size_t set_jintArray(const int* _cintArray, size_t _cintArrayLength, jintArray _jintArray)
    {
        jint* cArray = reinterpret_cast<jint*>(const_cast<int*>(_cintArray));
        jsize clen = static_cast<jsize>(_cintArrayLength);
        jsize jlen = _env.obj()->GetArrayLength(_jintArray);
        clen = _min(clen, jlen);
        _env.obj()->SetIntArrayRegion(_jintArray, 0, clen, cArray);

        return clen;
    }
    size_t array_copy(jbyteArray _jbyteArray, size_t offset, const ByteArray& _cbyteArray)
    {
        jboolean isCopy = JNI_FALSE;
        size_t clen = offset + _cbyteArray.GetLength();
        size_t jlen = _env.obj()->GetArrayLength(_jbyteArray);
        // java Array 缓冲区太小,会导致数组越界
        if(clen > jlen)
            return 0;
        jbyte* pByte = _env.obj()->GetByteArrayElements(_jbyteArray, &isCopy);
        if(pByte == NULL)
            return 0;
        clen = _cbyteArray.GetLength();
        _cbyteArray.ToArray(pByte + offset, clen);
        _env.obj()->ReleaseByteArrayElements(_jbyteArray, pByte, 0);
        return clen;
    }
    size_t array_copy(jintArray _jintArray, size_t offset, const int* _cintArray, size_t intlen)
    {
        jboolean isCopy = JNI_FALSE;
        size_t clen = offset + intlen;
        size_t jlen = _env.obj()->GetArrayLength(_jintArray);
        // java Array 缓冲区太小,会导致数组越界
        if(clen > jlen)
            return 0;
        jint* pInt = _env.obj()->GetIntArrayElements(_jintArray, &isCopy);
        if(pInt == NULL)
            return 0;
        memcpy(pInt + offset, _cintArray, intlen);
        _env.obj()->ReleaseIntArrayElements(_jintArray, pInt, 0);
        return intlen;
    }
    /// 获取UTF格式的字符串 
    inline jstring get_string(const char* str)
    {
        return _env.obj()->NewStringUTF(str);
    }
    /// 获取默认格式的字符串(Unicode)
    inline jstring get_string(const wchar_t* wstr)
    {
        wchar_t* p = const_cast<wchar_t*>(wstr);
        return _env.obj()->NewString(reinterpret_cast<const jchar*>(p), _wcslen(wstr));
    }
    /// 获取UTF格式的字符串 
    inline string get_string(jstring str)
    {
        jboolean isCopy = JNI_FALSE;
        const char* pStr =  _env.obj()->GetStringUTFChars(str, &isCopy);
        string s = pStr;
        _env.obj()->ReleaseStringUTFChars(str, pStr);
        return s;
    }
    /// 获取默认格式的字符串(Unicode) 
    inline wstring get_wstring(jstring str)
    {
        jboolean isCopy = JNI_FALSE;
        jchar* p = const_cast<jchar*>(_env.obj()->GetStringChars(str, &isCopy));
        const wchar_t* pStr = reinterpret_cast<const wchar_t*>(p);
        wstring s = pStr;
        _env.obj()->ReleaseStringChars(str, p);
        return s;
    }
    /// 将byte[]转换为字符串 
    inline string get_string(jbyteArray str)
    {
        jboolean isCopy = JNI_FALSE;
        jbyte* jArray = _env.obj()->GetByteArrayElements(str, &isCopy);
        const char* pStr = reinterpret_cast<const char*>(jArray);
        string s = pStr;
        _env.obj()->ReleaseByteArrayElements(str, jArray, 0);
        return s;
    }
};
//--------------------------------------------------------- 
/// Java对象
class JniInvoker
{
protected:
    /// Jni上层Java类对象
    shared_obj<jobject> _obj;
    /// Jni上层类
    shared_obj<jclass> _jcls;
    /// Jni Env指针
    shared_obj<JNIEnv*> _env;

    /// 初始化函数 
    inline void _init()
    {
        _env.obj() = NULL;
        _obj.obj() = NULL;
        _jcls.obj() = NULL;
    }
public:
    JniInvoker() { _init(); }
    JniInvoker(JNIEnv* env, jobject obj)
    {
        _init();
        Create(env, obj);
    }
    virtual ~JniInvoker() { Dispose(); }

    bool Create(JNIEnv* env, jobject obj)
    {
        if(env == NULL || obj == NULL)
            return false;

        jobject refObj = env->NewGlobalRef(obj);
        if(refObj == NULL)
            return false;
        
        jclass jcls = env->GetObjectClass(refObj);
        if(jcls == NULL)
        {
            env->DeleteGlobalRef(refObj);
            return false;
        }
        
        Dispose();

        _env.obj() = env;
        _obj.obj() = refObj;
        _jcls.obj() = jcls;

        return true;
    }
    inline bool IsValid() const
    {
        return _env.obj() != NULL;
    }
    void Dispose()
    {
        // 只有自身拥有对象 
        if(IsValid() && _env.ref_count() < 2)
        {
            if(_obj.obj() != NULL)
                _env.obj()->DeleteGlobalRef(_obj.obj());
        }
        _env.reset() = NULL;
        _obj.reset() = NULL;
        _jcls.reset() = NULL;
    }
    
    inline operator JNIEnv* () { return _env.obj(); }
    inline operator const JNIEnv* () const { return _env.obj(); }

    inline JNIEnv& operator* () { return *(_env.obj()); }
    inline JNIEnv* operator->() { return &(operator *()); }

    inline operator jclass () { return _jcls; }
    inline operator jobject () { return _obj; }
    inline operator const jclass () const { return _jcls; }
    inline operator const jobject () const { return _obj; }
};
//--------------------------------------------------------- 
/**
 * @brief Jni调用Java时的异常
 * @date 2019-03-05 15:37
 */
class JniThrowable
{
protected:
    JNIEnv* _jenv;
    jthrowable _jniExc;
public:
    JniThrowable() : _jenv(NULL), _jniExc(NULL) {}
    JniThrowable(JNIEnv* env) : _jenv(env), _jniExc(NULL) {}
    virtual ~JniThrowable() { Catch(); }

    /// 初始化,返回env是否有效
    inline bool Try(JNIEnv* env)
    {
        _jenv = env;
        return IsValid();
    }
    /// 返回对象是否有效
    inline bool IsValid() const
    {
        return _jenv != NULL;
    }
    /// 捕获异常状态,返回是否有异常发生
    bool Catch()
    {
        if(!IsValid())
            return false;
        _jniExc = _jenv->ExceptionOccurred();
        if(!_jniExc)
            return false;
        // 打印异常
        _jenv->ExceptionDescribe();
        _jenv->ExceptionClear();
        return true;
    }
    /// 抛出新异常,返回是否成功抛出
    bool Throw(const char* jClsName, const char* msg)
    {
        if(!IsValid() || _jniExc == NULL)
            return false;
        jclass exceptionCls = _jenv->FindClass(jClsName);
        if(!exceptionCls)
            return false;
        _jenv->ThrowNew(exceptionCls, msg);
        return true;
    }
    /// 抛出原来的异常
    bool Throw()
    {
        if(!IsValid() || _jniExc == NULL)
            return false;
        _jenv->Throw(_jniExc);
        return true;
    }
};
//--------------------------------------------------------- 
/**
 * @brief Jni对Java中本地引用的jobject封装
 * @date 2019-03-05 16:20
 */
class JniObject
{
protected:
    JNIEnv* _jenv;
    shared_obj<jobject> _jobj;

    bool _create(const ByteArray& data)
    {
        if(!_jenv)
            return false;
        jbyteArray byteArray = _jenv->NewByteArray(data.GetLength());
        if(!byteArray)
            return false;
        JniConverter(_jenv).set_jbyteArray(data, byteArray);
        _jobj.obj() = byteArray;
        return true;
    }
    bool _create(const char* str)
    {
        if(!_jenv)
            return false;
        jstring jString = _jenv->NewStringUTF(str);
        if(!jString)
            return false;
        _jobj.obj() = jString;
        return true;
    }
    bool _create(const int* arr, size_t size)
    {
        if(!_jenv)
            return false;
        jintArray intArray = _jenv->NewIntArray(size);
        if(!intArray)
            return false;
        JniConverter(_jenv).set_jintArray(arr, size, intArray);
        _jobj.obj() = intArray;
        return true;
    }
public:
    JniObject() : _jenv(NULL), _jobj(NULL) {}
    JniObject(JNIEnv* env, const ByteArray& data) :
        _jenv(env), _jobj(NULL)
    {
        _create(data);
    }
    JniObject(JNIEnv* env, const char* str) :
        _jenv(env), _jobj(NULL)
    {
        _create(str);
    }
    JniObject(JNIEnv* env, const int* arr, size_t size) :
        _jenv(env), _jobj(NULL)
    {
        _create(arr, size);
    }
    virtual ~JniObject()
    {
        Dispose();
    }
    bool Create(const ByteArray& data)
    {
        Dispose();
        return _create(data);
    }
    bool Create(const char* str)
    {
        Dispose();
        return _create(str);
    }
    bool Create(const int* arr, size_t size)
    {
        Dispose();
        return _create(arr, size);
    }
    inline operator jobject() const
    {
        return _jobj;
    }
    inline bool IsValid() const
    {
        return _jenv && _jobj.obj();
    }
    void Dispose()
    {
        if(IsValid() && _jobj.ref_count() < 2)
        {
            _jenv->DeleteLocalRef(_jobj);
        }
        _jobj.reset() = NULL;
    }
};
//--------------------------------------------------------- 
/**
 * @brief Jni调用Java时的传参辅助类
 * @date 2019-03-05 15:39
 */
class JniValue
{
protected:
    std::vector<jvalue> _args;

    inline jvalue& _put()
    {
        _args.push_back(jvalue());
        return _args.back();
    }
public:
    JniValue() {}
    JniValue(size_t count) : _args(count) {}

    inline JniValue& push()
    {
        return (*this);
    }
    JniValue& operator ,(bool val)
    {
        _put().z = val;
        return (*this);
    }
    JniValue& operator ,(byte val)
    {
        _put().b = val;
        return (*this);
    }
    JniValue& operator ,(char val)
    {
        _put().c = val;
        return (*this);
    }
    JniValue& operator ,(short val)
    {
        _put().s = val;
        return (*this);
    }
    JniValue& operator ,(int val)
    {
        _put().i = val;
        return (*this);
    }
    JniValue& operator ,(long val)
    {
        _put().j = val;
        return (*this);
    }
    JniValue& operator ,(float val)
    {
        _put().f = val;
        return (*this);
    }
    JniValue& operator ,(double val)
    {
        _put().d = val;
        return (*this);
    }
    JniValue& operator ,(jobject val)
    {
        _put().l = val;
        return (*this);
    }

    operator jvalue*() const
    {
        return const_cast<jvalue*>(&_args[0]);
    }
};
//--------------------------------------------------------- 
/// Jni与Java互操作辅助工具
class JniHelper
{
protected:
    JniHelper() {}
public:
    static bool Get(JniInvoker& jni, const char* property, jint& intVal)
    {
        jfieldID intID = jni->GetFieldID(jni, property, "I");
        if(intID == NULL)
            return false;
        intVal = ctype_cast(jint)jni->GetIntField(jni, intID);
        return true;
    }
    static bool Get(JniInvoker& jni, const char* property, string& strVal)
    {
        jclass jcls = jni;
        jfieldID strID = jni->GetFieldID(jcls, property, "Ljava/lang/String;");
        if(strID == NULL)
            return false;
        jstring jstr = ctype_cast(jstring)jni->GetObjectField(jcls, strID);
        strVal = JniConverter(jni).get_string(jstr);
        return true;
    }
    static bool Set(JniInvoker& jni, const char* property, jint intVal)
    {
        jclass jcls = jni;
        jfieldID intID = jni->GetFieldID(jcls, property, "I");
        if(intID == NULL)
            return false;
        jni->SetIntField(jcls, intID, intVal);
        return true;
    }
    static bool Set(JniInvoker& jni, const char* property, const char* strVal)
    {
        jclass jcls = jni;
        jfieldID strID = jni->GetFieldID(jcls, property, "Ljava/lang/String;");
        if(strID == NULL)
            return false;
        jni->SetObjectField(jcls, strID, JniConverter(jni).get_string(_strput(strVal)));
        return true;
    }
};
//--------------------------------------------------------- 
} // namespace ability 
} // namespace extension
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_JNIINVOKER_H_
//========================================================= 
